package com.ppch.zerocodegenerator.core;

import cn.hutool.json.JSONUtil;
import com.ppch.zerocodegenerator.ai.service.AiCodeGeneratorService;
import com.ppch.zerocodegenerator.ai.service_factory.AiCodeGeneratorServiceFactory;
import com.ppch.zerocodegenerator.ai.enums.CodeGenTypeEnum;
import com.ppch.zerocodegenerator.ai.model.code_result.HtmlCodeResult;
import com.ppch.zerocodegenerator.ai.model.code_result.MultiFileCodeResult;
import com.ppch.zerocodegenerator.ai.model.message.AiResponseStreamMessage;
import com.ppch.zerocodegenerator.ai.model.message.ToolExecutedStreamMessage;
import com.ppch.zerocodegenerator.ai.model.message.ToolRequestStreamMessage;
import com.ppch.zerocodegenerator.core.builder.VueProjectBuilderDev;
import com.ppch.zerocodegenerator.core.builder.VueProjectBuilderProd;
import com.ppch.zerocodegenerator.core.code_parser.CodeParserExecutor;
import com.ppch.zerocodegenerator.core.code_saver.CodeFileSaverExecutor;
import com.ppch.zerocodegenerator.exception.BusinessException;
import com.ppch.zerocodegenerator.exception.ErrorCode;
import com.ppch.zerocodegenerator.model.constant.AppConstant;
import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.BeforeToolExecution;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;

/**
 * AI 代码生成门面类，组合AI代码生成和保存为文件功能
 */
@Service
@Slf4j
public class AiCodeGeneratorFacade {

    @Resource
    private AiCodeGeneratorServiceFactory aiCodeGeneratorServiceFactory;

    /**
     * 流式响应
     * 统一入口：根据代码生成模式生成对应代码，然后从AI响应中解析出文本内容并保存到文件中
     * 注意：需要手动对AI响应进行解析，流式输出无法使用结构化输出
     *
     * @param userMessage     用户提示词
     * @param codeGenTypeEnum 生成类型
     * @param appId           应用ID
     */
    public Flux<String> generateAndSaveCodeWithStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成类型为空");
        }
        /**
         * 根据 appId、codeGenTypeEnum 从 Caffeine 本地缓存中获取 AiService 实例，如果缓存中没有则创建一个 AiService 实例。
         * 每个 AiService 实例拥有独立的对话记忆.
         */
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getOrCreateAiCodeGeneratorService(appId,codeGenTypeEnum);

        return switch (codeGenTypeEnum) {
            case HTML -> {
                //1.根据用户输入生成对应代码
                Flux<String> stringFlux = aiCodeGeneratorService.generateHtmlCodeWithStream(userMessage);
                //2.处理 AI 响应结果
                yield processCodeStream(stringFlux, codeGenTypeEnum, appId);
            }
            case MULTI_FILE -> {
                Flux<String> stringFlux = aiCodeGeneratorService.generateMultiFileCodeWithStream(userMessage);
                yield processCodeStream(stringFlux, codeGenTypeEnum, appId);
            }
            case VUE_PROJECT -> {
                TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeWithStream(userMessage, appId);
                yield processTokenStream(tokenStream,appId);
            }
            default -> {
                String errorMessage = "不支持的生成类型：" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
            }
        };
    }

    /**
     * 处理 TokenStream 流式代码，并转换为 Flux<String>
     *
     * @param tokenStream  代码流
     * @param appId       应用ID
     * @return 流式响应
     */
    private Flux<String> processTokenStream(TokenStream tokenStream,Long appId) {

        return Flux.create(sink -> {
            tokenStream
                    //1.监听 AI 流式响应
                    .onPartialResponse((String partialResponse) -> {
                        //将结果封装到对象中
                        AiResponseStreamMessage aiResponseStreamMessage = new AiResponseStreamMessage(partialResponse);
                        //将对象转换为Json，例如：{"type":"ai_response","data":"xxx"}
                        sink.next(JSONUtil.toJsonStr(aiResponseStreamMessage));
                    })
                    // 不知道为啥DeepSeek输出内容中不包含思考内容
                    /*.onPartialThinking((PartialThinking partialThinking) -> {
                        System.out.println("partialThinking = " + partialThinking);
                        sink.next(partialThinking.text());
                    })*/
                    //2.监听工具执行请求
                    // TODO：注意这个方法不支持流式输出
                    .beforeToolExecution((BeforeToolExecution beforeToolExecution) -> {
                        //获取工具执行请求对象
                        ToolExecutionRequest request = beforeToolExecution.request();
                        ToolRequestStreamMessage toolRequestMessage = new ToolRequestStreamMessage(request);
                        sink.next(JSONUtil.toJsonStr(toolRequestMessage));
                    })
                    //3.监听工具执行结果
                    //TODO：注意这个方法不支持流式输出
                    .onToolExecuted((ToolExecution toolExecution) -> {
                        ToolExecutedStreamMessage toolExecutedMessage = new ToolExecutedStreamMessage(toolExecution);
                        sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
                    })
                    //4.监听 AI 流式响应完成，并对 VUE 工程项目进行构建（安装依赖、打包项目）
                    .onCompleteResponse((ChatResponse response) -> {
                        sink.complete();
                        // TODO:同步构建 Vue 项目（安装项目依赖、项目打包）
                        String projectPath = AppConstant.CODE_OUTPUT_ROOT_DIR + "/vue_project_" + appId;
                        new VueProjectBuilderProd().buildProject(projectPath);
                    })
                    //5.监听错误
                    .onError((Throwable error) -> {
                        error.printStackTrace();
                        sink.error(error);
                    })
                    //开启Flux
                    .start();
        });
    }


    /**
     * 处理 Flux<String> 流式代码的方法
     * 1.根据代码生成模式使用对应的代码解析器解析代码
     * 2.根据代码生成模式使用对应的代码保存器保存代码
     * @param codeStream  代码流
     * @param codeGenType 代码生成类型
     * @param appId       应用ID
     * @return 流式响应
     */
    private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnum codeGenType, Long appId) {
        StringBuilder codeBuilder = new StringBuilder();
        return codeStream.doOnNext(chunk -> {
            // 实时收集代码片段
            codeBuilder.append(chunk);
        }).doOnComplete(() -> {
            // 流式返回完成后保存代码
            try {
                String completeCode = codeBuilder.toString();
                //2.使用执行器解析代码
                Object parsedResult = CodeParserExecutor.executeParser(completeCode, codeGenType);
                //3.使用执行器保存代码
                File savedDir = CodeFileSaverExecutor.executeSaver(parsedResult, codeGenType, appId);
                log.info("保存成功，路径为：" + savedDir.getAbsolutePath());
            } catch (Exception e) {
                log.error("保存失败: {}", e.getMessage());
            }
        });
    }

    /**
     * 普通响应
     * 统一入口：根据代码生成模式生成对应代码并保存到文件中
     * 注意：无需对AI响应进行解析，因为使用了结构化输出，直接从对象中获取对应属性值然后保存为文件即可
     *
     * @param userMessage     用户提示词
     * @param codeGenTypeEnum 生成类型
     * @param appId           应用ID
     * @return 保存的目录
     */
    public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "代码生成模式不可为空");
        }
        /**
         * 根据 appId 先从 Caffeine 本地缓存中获取 AiService 实例，如果缓存中没有则创建一个 AiService 实例。
         * 每个 AiService 实例拥有独立的对话记忆.
         */
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getOrCreateAiCodeGeneratorService(appId);

        return switch (codeGenTypeEnum) {
            case HTML -> {
                //1.根据用户输入生成对应代码
                HtmlCodeResult htmlCodeResult = aiCodeGeneratorService.generateHtmlCode(userMessage);
                //2.保存代码到文件
                yield CodeFileSaverExecutor.executeSaver(htmlCodeResult, codeGenTypeEnum, appId);
            }
            case MULTI_FILE -> {
                //1.根据用户输入生成对应代码
                MultiFileCodeResult multiFileCodeResult = aiCodeGeneratorService.generateMultiFileCode(userMessage);
                //2.保存代码到文件
                yield CodeFileSaverExecutor.executeSaver(multiFileCodeResult, codeGenTypeEnum, appId);
            }
            default -> {
                String errorMessage = "不支持的生成类型：" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
            }
        };
    }


    /**
     * 发送 “工具正在执行” 事件，给用户一个友好提示
     */
    private void sendToolExecutionEvent() {

    }


}
